package com.niodata.dp.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.DurationFieldType;

/**
 * a simple seq generate util.
 */
public class SequenceUtil {


  /**
   * make time seq by duration interval.
   *
   * @param origin origin date
   * @param count item count
   * @param duration duration interval
   * @return list of date
   */
  public static List<Date> makeTimeSeq(Date origin,  int count, Duration duration) {
    List<Date> list = new ArrayList<>();
    list.add(origin);
    DateTime dt = new DateTime(origin);
    for (int i = 0; i < count; i++) {
      dt = dt.withDurationAdded(duration, 1);
      list.add(dt.toDate());
    }
    if (list.size() > count) {
      list = list.subList(0, list.size() - 1);
    }
    return list;
  }

  /**
   * make time seq by duration type and interval.
   *
   * @param origin origin time
   * @param durationFieldType duration type
   * @param count item count
   * @param step interval count
   * @return list of time
   */
  public static List<Date> makeTimeSeq(Date origin, DurationFieldType durationFieldType, int count, int step) {
    List<Date> list = new ArrayList<>();
    list.add(origin);
    DateTime dt = new DateTime(origin);
    for (int i = 0; i < count; i++) {
      dt = dt.withFieldAdded(durationFieldType, step);
      list.add(dt.toDate());
    }
    if (list.size() > count) {
      list = list.subList(0, list.size() - 1);
    }
    return list;
  }

  /**
   * make time seq by duration.
   *
   * @param origin origin date
   * @param maxTime max time
   * @param duration time duration
   * @return list of date
   */
  public static List<Date> makeTimeSeqByRange(Date origin, Date maxTime, Duration duration) {
    List<Date> list = new ArrayList<>();
    DateTime dt = new DateTime(origin);
    Date t = origin;
    list.add(t);
    while (t.compareTo(maxTime) < 0) {
      dt = dt.withDurationAdded(duration, 1);
      t = dt.toDate();
      if (t.compareTo(maxTime) <= 0) {
        list.add(t);
      }
    }
    return list;
  }


  /**
   * make time seq by time range and duration.
   *
   * @param origin origin date
   * @param maxTime max time
   * @param durationFieldType duration type
   * @param step interval
   * @return list of date
   */
  public static List<Date> makeTimeSeqByRange(Date origin, Date maxTime,
                                              DurationFieldType durationFieldType,
                                              int step) {
    if (step > 0 && origin.compareTo(maxTime) > 0) {
      return Collections.emptyList();
    }
    if (step < 0 && origin.compareTo(maxTime) < 0) {
      return Collections.emptyList();
    }
    List<Date> list = new ArrayList<>();
    DateTime dt = new DateTime(origin);
    Date t = origin;
    list.add(t);
    if (step > 0) {
      while (t.compareTo(maxTime) < 0) {
        dt = dt.withFieldAdded(durationFieldType, step);
        t = dt.toDate();
        if (t.compareTo(maxTime) <= 0) {
          list.add(t);
        }

      }
    }
    if (step < 0) {
      while (t.compareTo(maxTime) >= 0) {
        dt = dt.withFieldAdded(durationFieldType, step);
        t = dt.toDate();
        if (t.compareTo(maxTime) > 0) {
          list.add(t);
        }
      }
    }

    return list;
  }

  /**
   * mage int seq.
   *
   * @param origin origin value
   * @param step interval
   * @param count item count
   * @return list of integer
   */
  public static List<Integer> makeIntSeq(int origin, int step, int count) {
    List<Integer> list = new ArrayList<>();
    list.add(origin);
    for (int i = 1; i < count; i++) {
      list.add(i * step);
    }
    if (list.size() > count) {
      list = list.subList(0, list.size() - 1);
    }
    return list;
  }

  /**
   * make integer seq by range.
   *
   * @param origin origin value
   * @param max max value
   * @param step interval
   * @return list of integer
   */
  public static List<Integer> makeIntSeqByRange(int origin, int max, int step) {
    if (step > 0 && origin > max) {
      return Collections.emptyList();
    }
    if (step < 0 && origin < max) {
      return Collections.emptyList();
    }
    List<Integer> list = new ArrayList<>();
    list.add(origin);
    int index = 1;
    int t = origin;
    if (step > 0) {
      while (t < max) {
        t = t + index * step;
        if (t < max) {
          list.add(t);
        }
        index++;
      }
    }
    if (step < 0) {
      while (t > max) {
        t = t + index * step;
        if (t > max) {
          list.add(t);
        }
        index++;
      }
    }

    return list;
  }

  /**
   * make double seq.
   *
   * @param origin origin value
   * @param step interval
   * @param count item count
   * @return list of double
   */
  public static List<Double> makeDoubleSeq(double origin, double step, int count) {
    List<Double> list = new ArrayList<>();
    list.add(origin);
    for (int i = 1; i < count; i++) {
      list.add(i * step);
    }
    if (list.size() > count) {
      list = list.subList(0, list.size() - 1);
    }
    return list;
  }

  /**
   * make double seq by range.
   *
   * @param origin origin value
   * @param max max
   * @param step interval
   * @return list of double
   */
  public static List<Double> makeDoubleSeqByRange(double origin, double max, double step) {
    if (step > 0 && origin > max) {
      return Collections.emptyList();
    }
    if (step < 0 && origin < max) {
      return Collections.emptyList();
    }
    List<Double> list = new ArrayList<>();
    list.add(origin);
    int index = 1;
    double t = origin;
    if (step > 0) {
      while (t < max) {
        t = t + index * step;
        if (t < max) {
          list.add(t);
        }
        index++;
      }
    }
    if (step < 0) {
      while (t > max) {
        t = t + index * step;
        if (t > max) {
          list.add(t);
        }
        index++;
      }
    }

    return list;
  }
}
